[iOS] Refactoring of subscription screens

https://jira.mail.ru/browse/MAPSME-14170
This commit is contained in:
Alexander Boriskov 2020-07-08 14:42:14 +03:00 committed by Aleksey Belousov
parent a5129893d8
commit bb7e48e009
21 changed files with 683 additions and 371 deletions

View file

@ -23,7 +23,7 @@
override func awakeFromNib() {
super.awakeFromNib()
switch screenType {
case .sightseeing:
case .city:
titleLabel.text = L("subscription_success_dialog_title_sightseeing_pass")
textLabel.text = L("subscription_success_dialog_message_sightseeing_pass")
case .allPass:

View file

@ -21,7 +21,7 @@ class SubscriptionSuccessViewController: UIViewController {
override func awakeFromNib() {
super.awakeFromNib()
switch screenType {
case .sightseeing:
case .city:
titleLabel.text = L("subscription_success_dialog_title_sightseeing_pass")
textLabel.text = L("subscription_success_dialog_message_sightseeing_pass")
case .allPass:

View file

@ -49,7 +49,7 @@ class PaidRouteViewController: MWMViewController {
self.statistics = statistics
self.subscriptionType = subscriptionType
switch subscriptionType {
case .sightseeing:
case .city:
self.subscriptionManager = InAppPurchase.bookmarksSubscriptionManager
case .allPass:
self.subscriptionManager = InAppPurchase.allPassSubscriptionManager

View file

@ -1,181 +0,0 @@
import SafariServices
class BaseSubscriptionViewController: MWMViewController {
//MARK: base outlets
@IBOutlet private var loadingView: UIView!
//MARK: dependency
private(set) var subscriptionManager: ISubscriptionManager?
private let bookmarksManager = BookmarksManager.shared()
private var subscriptionGroup: ISubscriptionGroup?
@objc var onSubscribe: MWMVoidBlock?
@objc var onCancel: MWMVoidBlock?
@objc var source: String = kStatWebView
private let transitioning = FadeTransitioning<IPadModalPresentationController>()
override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return [.portrait] }
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
if UIDevice.current.userInterfaceIdiom == .pad {
transitioningDelegate = transitioning
modalPresentationStyle = .custom
} else {
modalPresentationStyle = .fullScreen
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
subscriptionManager?.removeListener(self)
}
override func viewDidLoad() {
super.viewDidLoad()
loadingView.isHidden = false
}
func configure(buttons: [SubscriptionPeriod: BookmarksSubscriptionButton],
discountLabels: [SubscriptionPeriod: InsetsLabel]) {
subscriptionManager?.getAvailableSubscriptions { [weak self] subscriptions, error in
self?.loadingView.isHidden = true
guard let subscriptions = subscriptions, subscriptions.count >= buttons.count else {
MWMAlertViewController.activeAlert().presentInfoAlert(L("price_error_title"),
text: L("price_error_subtitle"))
self?.onCancel?()
return
}
let group = SubscriptionGroup(subscriptions: subscriptions)
self?.subscriptionGroup = group
for (period, button) in buttons {
if let subscriptionItem = group[period] {
button.config(title: subscriptionItem.title,
price: subscriptionItem.formattedPrice,
enabled: true)
if subscriptionItem.hasDiscount, let discountLabel = discountLabels[period] {
discountLabel.isHidden = false
discountLabel.text = L("all_pass_screen_best_value")
}
}
}
}
Statistics.logEvent(kStatInappShow, withParameters: [kStatVendor: subscriptionManager?.vendorId ?? "",
kStatPurchase: subscriptionManager?.serverId ?? "",
kStatProduct: subscriptionManager?.productIds[0] ?? "",
kStatFrom: source], with: .realtime)
}
func purchase(sender: UIButton, period: SubscriptionPeriod) {
subscriptionManager?.addListener(self)
guard let subscription = subscriptionGroup?[period]?.subscription else {
return
}
signup(anchor: sender, source: .subscription) { [weak self] success in
guard success else { return }
self?.loadingView.isHidden = false
self?.bookmarksManager.ping { success in
guard success else {
self?.loadingView.isHidden = true
let errorDialog = SubscriptionFailViewController { [weak self] in
self?.dismiss(animated: true)
}
self?.present(errorDialog, animated: true)
return
}
self?.subscriptionManager?.subscribe(to: subscription)
}
}
Statistics.logEvent(kStatInappSelect, withParameters: [kStatPurchase: subscriptionManager?.serverId ?? "",
kStatProduct: subscription.productId],
with: .realtime)
Statistics.logEvent(kStatInappPay, withParameters: [kStatPurchase: subscriptionManager?.serverId ?? ""],
with: .realtime)
}
@IBAction func onRestore(_ sender: UIButton) {
subscriptionManager?.addListener(self)
Statistics.logEvent(kStatInappRestore, withParameters: [kStatPurchase: subscriptionManager?.serverId ?? ""])
signup(anchor: sender, source: .subscription) { [weak self] success in
guard success else { return }
self?.loadingView.isHidden = false
self?.subscriptionManager?.restore { result in
self?.loadingView.isHidden = true
let alertText: String
switch result {
case .valid:
alertText = L("restore_success_alert")
case .notValid:
alertText = L("restore_no_subscription_alert")
case .serverError, .authError:
alertText = L("restore_error_alert")
}
MWMAlertViewController.activeAlert().presentInfoAlert(L("restore_subscription"), text: alertText)
}
}
}
@IBAction func onClose(_ sender: UIButton) {
onCancel?()
Statistics.logEvent(kStatInappCancel, withParameters: [kStatPurchase: subscriptionManager?.serverId ?? ""])
}
@IBAction func onTerms(_ sender: UIButton) {
guard let url = URL(string: User.termsOfUseLink()) else { return }
let safari = SFSafariViewController(url: url)
present(safari, animated: true, completion: nil)
}
@IBAction func onPrivacy(_ sender: UIButton) {
guard let url = URL(string: User.privacyPolicyLink()) else { return }
let safari = SFSafariViewController(url: url)
present(safari, animated: true, completion: nil)
}
}
extension BaseSubscriptionViewController: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
onCancel?()
}
}
extension BaseSubscriptionViewController: SubscriptionManagerListener {
func didFailToValidate() {
loadingView.isHidden = true
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didValidate(_ isValid: Bool) {
loadingView.isHidden = true
if isValid {
onSubscribe?()
} else {
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
}
func didFailToSubscribe(_ subscription: ISubscription, error: Error?) {
loadingView.isHidden = true
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didSubscribe(_ subscription: ISubscription) {
subscriptionManager?.setSubscriptionActive(true)
bookmarksManager.resetInvalidCategories()
}
func didDefer(_ subscription: ISubscription) {
}
}

View file

@ -1,53 +0,0 @@
import SafariServices
@objc class BookmarksSubscriptionViewController: BaseSubscriptionViewController {
//MARK: outlets
@IBOutlet private var annualSubscriptionButton: BookmarksSubscriptionButton!
@IBOutlet private var annualDiscountLabel: InsetsLabel!
@IBOutlet private var monthlySubscriptionButton: BookmarksSubscriptionButton!
@IBOutlet private var contentView: UIView!
override var subscriptionManager: ISubscriptionManager? {
get { return InAppPurchase.bookmarksSubscriptionManager }
}
override var preferredStatusBarStyle: UIStatusBarStyle {
get { return UIColor.isNightMode() ? .lightContent : .default }
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
annualSubscriptionButton.config(title: L("annual_subscription_title"),
price: "...",
enabled: false)
monthlySubscriptionButton.config(title: L("montly_subscription_title"),
price: "...",
enabled: false)
annualDiscountLabel.isHidden = true
self.configure(buttons: [
.year: annualSubscriptionButton,
.month: monthlySubscriptionButton],
discountLabels:[
.year: annualDiscountLabel])
self.preferredContentSize = CGSize(width: 414, height: contentView.frame.height)
}
@IBAction func onAnnualButtonTap(_ sender: UIButton) {
purchase(sender: sender, period: .year)
}
@IBAction func onMonthlyButtonTap(_ sender: UIButton) {
purchase(sender: sender, period: .month)
}
}

View file

@ -1,51 +0,0 @@
class SubscriptionViewBuilder {
enum SuccessDialog {
case goToCatalog
case success
case none
}
static func build(type: SubscriptionGroupType,
parentViewController: UIViewController,
source: String,
successDialog: SuccessDialog,
completion: ((Bool) -> Void)?) -> UIViewController {
let subscribeViewController: BaseSubscriptionViewController
switch type {
case .allPass:
subscribeViewController = AllPassSubscriptionViewController()
case .sightseeing:
subscribeViewController = BookmarksSubscriptionViewController()
}
subscribeViewController.source = source
subscribeViewController.onSubscribe = {
parentViewController.dismiss(animated: true) {
completion?(true);
}
switch successDialog {
case .goToCatalog:
let successDialog = SubscriptionGoToCatalogViewController(type, onOk: {
parentViewController.dismiss(animated: true)
let webViewController = CatalogWebViewController.catalogFromAbsoluteUrl(nil, utm: .none)
parentViewController.navigationController?.pushViewController(webViewController, animated: true)
}) {
parentViewController.dismiss(animated: true)
}
parentViewController.present(successDialog, animated: true)
case .success:
let successDialog = SubscriptionSuccessViewController(type) {
parentViewController.dismiss(animated: true)
}
parentViewController.present(successDialog, animated: true)
case .none:
break;
}
}
subscribeViewController.onCancel = {
parentViewController.dismiss(animated: true) {
completion?(false)
}
}
return subscribeViewController
}
}

View file

@ -7,14 +7,16 @@ extension UIViewController {
let onSubscribe = {
self?.dismiss(animated: true)
let subscriptionDialog = AllPassSubscriptionViewController()
subscriptionDialog.onSubscribe = { [weak self] in
self?.dismiss(animated: true)
guard let parentViewController = self else {
return
}
subscriptionDialog.onCancel = { [weak self] in
self?.dismiss(animated: true) {
self?.checkInvalidSubscription(completion)
}
let subscriptionDialog = AllPassSubscriptionBuilder.build(parentViewController: parentViewController,
source: kStatWebView,
successDialog: .none,
subscriptionGroupType: .allPass) { (result) in
if (!result) {
self?.checkInvalidSubscription(completion)
}
}
self?.present(subscriptionDialog, animated: true)
completion?(false)

View file

@ -1,11 +1,11 @@
@objc enum SubscriptionGroupType: Int {
case allPass
case sightseeing
case city
init?(serverId: String) {
switch serverId {
case MWMPurchaseManager.bookmarksSubscriptionServerId():
self = .sightseeing
self = .city
case MWMPurchaseManager.allPassSubscriptionServerId():
self = .allPass
default:
@ -25,7 +25,7 @@
if subscriptionGroups?.first(where: { $0 == MWMPurchaseManager.allPassSubscriptionServerId() }) != nil {
self = .allPass
} else if subscriptionGroups?.first(where: { $0 == MWMPurchaseManager.bookmarksSubscriptionServerId() }) != nil {
self = .sightseeing
self = .city
} else {
self = .allPass
}
@ -33,7 +33,7 @@
var serverId: String {
switch self {
case .sightseeing:
case .city:
return MWMPurchaseManager.bookmarksSubscriptionServerId()
case .allPass:
return MWMPurchaseManager.allPassSubscriptionServerId()

View file

@ -380,9 +380,8 @@
479D306822C66C8F00D18278 /* MWMBookmarksBannerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 479D306722C66C8F00D18278 /* MWMBookmarksBannerViewController.m */; };
479EE94A2292FB03009DEBA6 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 479EE9492292FB03009DEBA6 /* ActivityIndicator.swift */; };
47A65CAD2350044800DCD85F /* CoreApi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47A65CAC2350044800DCD85F /* CoreApi.framework */; };
47A6F3C4235F47B90053FBA4 /* BookmarksSubscriptionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 47A6F3C1235F47B90053FBA4 /* BookmarksSubscriptionViewController.xib */; };
47A6F3C4235F47B90053FBA4 /* CitySubscriptionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 47A6F3C1235F47B90053FBA4 /* CitySubscriptionViewController.xib */; };
47A6F3C5235F47B90053FBA4 /* BookmarksSubscriptionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47A6F3C2235F47B90053FBA4 /* BookmarksSubscriptionButton.swift */; };
47A6F3C6235F47B90053FBA4 /* BookmarksSubscriptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47A6F3C3235F47B90053FBA4 /* BookmarksSubscriptionViewController.swift */; };
47AEF8402231249E00D20538 /* categories_brands.txt in Resources */ = {isa = PBXBuildFile; fileRef = 47AEF83F2231249E00D20538 /* categories_brands.txt */; };
47B06DED21B696C20094CCAD /* GeoTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DEC21B696C20094CCAD /* GeoTracker.swift */; };
47B06DF021B697230094CCAD /* MWMGeoTrackerCore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DEF21B697230094CCAD /* MWMGeoTrackerCore.mm */; };
@ -621,12 +620,10 @@
99514BB823E82B450085D3A7 /* ElevationProfilePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99514BB223E82B450085D3A7 /* ElevationProfilePresenter.swift */; };
99514BBA23E82B450085D3A7 /* ElevationProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99514BB423E82B450085D3A7 /* ElevationProfileViewController.swift */; };
99514BBB23E82B450085D3A7 /* ElevationProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99514BB523E82B450085D3A7 /* ElevationProfileBuilder.swift */; };
99536111235DABB1008B218F /* BaseSubscriptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99536110235DABB1008B218F /* BaseSubscriptionViewController.swift */; };
99536113235DB86C008B218F /* InsetsLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99536112235DB86C008B218F /* InsetsLabel.swift */; };
995738DB235484410019AEE7 /* AllPassSubscriptionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 995738DA235484410019AEE7 /* AllPassSubscriptionViewController.xib */; };
995739042355CAA30019AEE7 /* PageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 995739032355CAA30019AEE7 /* PageIndicator.swift */; };
995739062355CAC40019AEE7 /* ImageViewCrossDisolve.swift in Sources */ = {isa = PBXBuildFile; fileRef = 995739052355CAC40019AEE7 /* ImageViewCrossDisolve.swift */; };
995739082355CB660019AEE7 /* AllPassSubscriptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 995739072355CB660019AEE7 /* AllPassSubscriptionViewController.swift */; };
9959C75624582DA2008FD4FD /* DirectionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9959C75524582DA2008FD4FD /* DirectionView.xib */; };
9959C75C24599CCD008FD4FD /* DirectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9959C75B24599CCC008FD4FD /* DirectionView.swift */; };
995F1613244F0AA50060631D /* BottomMenuLayersCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 995F1611244F0AA40060631D /* BottomMenuLayersCell.swift */; };
@ -674,6 +671,11 @@
99B6A74C2362F5AA002C94CB /* PromoButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B6A74B2362F5AA002C94CB /* PromoButton.swift */; };
99B6A74E2362F5CD002C94CB /* PromoCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B6A74D2362F5CD002C94CB /* PromoCoordinator.swift */; };
99B6A77F23684573002C94CB /* PromoDiscoveryBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B6A77E23684573002C94CB /* PromoDiscoveryBuilder.swift */; };
99BFEF0124B48D7600A65F5B /* SubscriptionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99BFEEFB24B48D7600A65F5B /* SubscriptionPresenter.swift */; };
99BFEF0224B48D7600A65F5B /* SubscriptionRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99BFEEFC24B48D7600A65F5B /* SubscriptionRouter.swift */; };
99BFEF0324B48D7600A65F5B /* CitySubscriptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99BFEEFD24B48D7600A65F5B /* CitySubscriptionViewController.swift */; };
99BFEF0424B48D7600A65F5B /* CitySubscriptionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99BFEEFE24B48D7600A65F5B /* CitySubscriptionBuilder.swift */; };
99BFEF0524B48D7600A65F5B /* SubscriptionInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99BFEEFF24B48D7600A65F5B /* SubscriptionInteractor.swift */; };
99C6532223F2F506004322F3 /* IPlacePageLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99C6532123F2F506004322F3 /* IPlacePageLayout.swift */; };
99C964292428C0F700E41723 /* PlacePageHeaderPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99C964232428C0F700E41723 /* PlacePageHeaderPresenter.swift */; };
99C9642B2428C0F700E41723 /* PlacePageHeaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99C964252428C0F700E41723 /* PlacePageHeaderViewController.swift */; };
@ -698,6 +700,9 @@
99E2B0122368A8C700FFABC5 /* MWMCategory+PlacesCountTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99E2B0112368A8C700FFABC5 /* MWMCategory+PlacesCountTitle.swift */; };
99E2B01E23698B0800FFABC5 /* WelcomeProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99E2B01D23698B0800FFABC5 /* WelcomeProtocols.swift */; };
99E2B0232369904800FFABC5 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99E2B0222369904800FFABC5 /* WelcomeViewController.swift */; };
99EBF72E24B4C89000FE1F1F /* AllPassSubscriptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99EBF72824B4C88F00FE1F1F /* AllPassSubscriptionViewController.swift */; };
99EBF73024B4C89000FE1F1F /* AllPassSubscriptionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99EBF72A24B4C88F00FE1F1F /* AllPassSubscriptionBuilder.swift */; };
99EBF73324B4C91000FE1F1F /* SubscriptionViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99EBF73224B4C91000FE1F1F /* SubscriptionViewProtocol.swift */; };
99F3EB0323F4178200C713F8 /* PlacePageCommonLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F3EB0223F4178200C713F8 /* PlacePageCommonLayout.swift */; };
99F3EB0623F418A200C713F8 /* PlacePagePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F3EB0523F418A200C713F8 /* PlacePagePresenter.swift */; };
99F3EB1123F418C900C713F8 /* PlacePageBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F3EB0B23F418C900C713F8 /* PlacePageBuilder.swift */; };
@ -1501,9 +1506,8 @@
479D306722C66C8F00D18278 /* MWMBookmarksBannerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMBookmarksBannerViewController.m; sourceTree = "<group>"; };
479EE9492292FB03009DEBA6 /* ActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ActivityIndicator.swift; path = CustomViews/ActivityIndicator.swift; sourceTree = "<group>"; };
47A65CAC2350044800DCD85F /* CoreApi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreApi.framework; sourceTree = BUILT_PRODUCTS_DIR; };
47A6F3C1235F47B90053FBA4 /* BookmarksSubscriptionViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BookmarksSubscriptionViewController.xib; sourceTree = "<group>"; };
47A6F3C1235F47B90053FBA4 /* CitySubscriptionViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CitySubscriptionViewController.xib; sourceTree = "<group>"; };
47A6F3C2235F47B90053FBA4 /* BookmarksSubscriptionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksSubscriptionButton.swift; sourceTree = "<group>"; };
47A6F3C3235F47B90053FBA4 /* BookmarksSubscriptionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksSubscriptionViewController.swift; sourceTree = "<group>"; };
47AEF83F2231249E00D20538 /* categories_brands.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = categories_brands.txt; path = ../../data/categories_brands.txt; sourceTree = "<group>"; };
47B06DEC21B696C20094CCAD /* GeoTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoTracker.swift; sourceTree = "<group>"; };
47B06DEE21B697230094CCAD /* MWMGeoTrackerCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMGeoTrackerCore.h; sourceTree = "<group>"; };
@ -1707,12 +1711,10 @@
99514BB223E82B450085D3A7 /* ElevationProfilePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElevationProfilePresenter.swift; sourceTree = "<group>"; };
99514BB423E82B450085D3A7 /* ElevationProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElevationProfileViewController.swift; sourceTree = "<group>"; };
99514BB523E82B450085D3A7 /* ElevationProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElevationProfileBuilder.swift; sourceTree = "<group>"; };
99536110235DABB1008B218F /* BaseSubscriptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseSubscriptionViewController.swift; sourceTree = "<group>"; };
99536112235DB86C008B218F /* InsetsLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsetsLabel.swift; sourceTree = "<group>"; };
995738DA235484410019AEE7 /* AllPassSubscriptionViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AllPassSubscriptionViewController.xib; sourceTree = "<group>"; };
995739032355CAA30019AEE7 /* PageIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIndicator.swift; sourceTree = "<group>"; };
995739052355CAC40019AEE7 /* ImageViewCrossDisolve.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewCrossDisolve.swift; sourceTree = "<group>"; };
995739072355CB660019AEE7 /* AllPassSubscriptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPassSubscriptionViewController.swift; sourceTree = "<group>"; };
9957FAE0237AE04900855F48 /* MWMMapViewControlsManager+AddPlace.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMMapViewControlsManager+AddPlace.h"; sourceTree = "<group>"; };
9959C75524582DA2008FD4FD /* DirectionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DirectionView.xib; sourceTree = "<group>"; };
9959C75B24599CCC008FD4FD /* DirectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DirectionView.swift; sourceTree = "<group>"; };
@ -1763,6 +1765,11 @@
99B6A74B2362F5AA002C94CB /* PromoButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromoButton.swift; sourceTree = "<group>"; };
99B6A74D2362F5CD002C94CB /* PromoCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromoCoordinator.swift; sourceTree = "<group>"; };
99B6A77E23684573002C94CB /* PromoDiscoveryBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromoDiscoveryBuilder.swift; sourceTree = "<group>"; };
99BFEEFB24B48D7600A65F5B /* SubscriptionPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPresenter.swift; sourceTree = "<group>"; };
99BFEEFC24B48D7600A65F5B /* SubscriptionRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionRouter.swift; sourceTree = "<group>"; };
99BFEEFD24B48D7600A65F5B /* CitySubscriptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CitySubscriptionViewController.swift; sourceTree = "<group>"; };
99BFEEFE24B48D7600A65F5B /* CitySubscriptionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CitySubscriptionBuilder.swift; sourceTree = "<group>"; };
99BFEEFF24B48D7600A65F5B /* SubscriptionInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionInteractor.swift; sourceTree = "<group>"; };
99C6532123F2F506004322F3 /* IPlacePageLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPlacePageLayout.swift; sourceTree = "<group>"; };
99C964232428C0F700E41723 /* PlacePageHeaderPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePageHeaderPresenter.swift; sourceTree = "<group>"; };
99C964252428C0F700E41723 /* PlacePageHeaderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePageHeaderViewController.swift; sourceTree = "<group>"; };
@ -1787,6 +1794,9 @@
99E2B0112368A8C700FFABC5 /* MWMCategory+PlacesCountTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MWMCategory+PlacesCountTitle.swift"; sourceTree = "<group>"; };
99E2B01D23698B0800FFABC5 /* WelcomeProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeProtocols.swift; sourceTree = "<group>"; };
99E2B0222369904800FFABC5 /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = "<group>"; };
99EBF72824B4C88F00FE1F1F /* AllPassSubscriptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPassSubscriptionViewController.swift; sourceTree = "<group>"; };
99EBF72A24B4C88F00FE1F1F /* AllPassSubscriptionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPassSubscriptionBuilder.swift; sourceTree = "<group>"; };
99EBF73224B4C91000FE1F1F /* SubscriptionViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionViewProtocol.swift; sourceTree = "<group>"; };
99F3EB0223F4178200C713F8 /* PlacePageCommonLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePageCommonLayout.swift; sourceTree = "<group>"; };
99F3EB0523F418A200C713F8 /* PlacePagePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePagePresenter.swift; sourceTree = "<group>"; };
99F3EB0B23F418C900C713F8 /* PlacePageBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePageBuilder.swift; sourceTree = "<group>"; };
@ -3802,20 +3812,6 @@
path = Theme;
sourceTree = "<group>";
};
995738D9235481FE0019AEE7 /* Subscription */ = {
isa = PBXGroup;
children = (
47A6F3C2235F47B90053FBA4 /* BookmarksSubscriptionButton.swift */,
47A6F3C3235F47B90053FBA4 /* BookmarksSubscriptionViewController.swift */,
47A6F3C1235F47B90053FBA4 /* BookmarksSubscriptionViewController.xib */,
995738DA235484410019AEE7 /* AllPassSubscriptionViewController.xib */,
995739072355CB660019AEE7 /* AllPassSubscriptionViewController.swift */,
99536110235DABB1008B218F /* BaseSubscriptionViewController.swift */,
999D3A66237BFA4600C5F7A8 /* SubscriptionViewBuilder.swift */,
);
path = Subscription;
sourceTree = "<group>";
};
995739022355CA5D0019AEE7 /* Pages */ = {
isa = PBXGroup;
children = (
@ -3879,6 +3875,32 @@
path = PromoDiscovery;
sourceTree = "<group>";
};
99BFEEFA24B48D3900A65F5B /* Subscription */ = {
isa = PBXGroup;
children = (
999D3A66237BFA4600C5F7A8 /* SubscriptionViewBuilder.swift */,
99BFEEFB24B48D7600A65F5B /* SubscriptionPresenter.swift */,
99BFEEFC24B48D7600A65F5B /* SubscriptionRouter.swift */,
99BFEEFF24B48D7600A65F5B /* SubscriptionInteractor.swift */,
99EBF73224B4C91000FE1F1F /* SubscriptionViewProtocol.swift */,
99EBF72524B4B54500FE1F1F /* Components */,
99EBF72424B4B53800FE1F1F /* AllPass */,
99BFEF0824B48D8600A65F5B /* City */,
);
name = Subscription;
path = ../../../xcode/geometry/Subscription;
sourceTree = "<group>";
};
99BFEF0824B48D8600A65F5B /* City */ = {
isa = PBXGroup;
children = (
99BFEEFD24B48D7600A65F5B /* CitySubscriptionViewController.swift */,
99BFEEFE24B48D7600A65F5B /* CitySubscriptionBuilder.swift */,
47A6F3C1235F47B90053FBA4 /* CitySubscriptionViewController.xib */,
);
path = City;
sourceTree = "<group>";
};
99C6531F23F2F178004322F3 /* Components */ = {
isa = PBXGroup;
children = (
@ -3992,6 +4014,24 @@
path = TermsOfUse;
sourceTree = "<group>";
};
99EBF72424B4B53800FE1F1F /* AllPass */ = {
isa = PBXGroup;
children = (
99EBF72824B4C88F00FE1F1F /* AllPassSubscriptionViewController.swift */,
99EBF72A24B4C88F00FE1F1F /* AllPassSubscriptionBuilder.swift */,
995738DA235484410019AEE7 /* AllPassSubscriptionViewController.xib */,
);
path = AllPass;
sourceTree = "<group>";
};
99EBF72524B4B54500FE1F1F /* Components */ = {
isa = PBXGroup;
children = (
47A6F3C2235F47B90053FBA4 /* BookmarksSubscriptionButton.swift */,
);
path = Components;
sourceTree = "<group>";
};
99F3EB0423F417BE00C713F8 /* PlacePageManager */ = {
isa = PBXGroup;
children = (
@ -4016,7 +4056,6 @@
isa = PBXGroup;
children = (
47C8788D22DF522B00A772DA /* Dialogs */,
995738D9235481FE0019AEE7 /* Subscription */,
B32FE73E20D2844600EF7446 /* DownloadedBookmarksViewController.swift */,
B32FE73F20D2844600EF7446 /* DownloadedBookmarksViewController.xib */,
B32FE74220D2B09600EF7446 /* CatalogWebViewController.swift */,
@ -4397,6 +4436,7 @@
F6E2FBFB1E097B9F0083EBEC /* UI */ = {
isa = PBXGroup;
children = (
99BFEEFA24B48D3900A65F5B /* Subscription */,
CDB4D4DA222D24EE00104869 /* CarPlay */,
F6E407CC1FC45ED4001F7821 /* Discovery */,
3432E17F1E49BEFA008477E9 /* Ads */,
@ -5093,7 +5133,7 @@
6741A9951BF340DE002C974C /* MWMDownloaderDialogCell.xib in Resources */,
6741A9511BF340DE002C974C /* MWMDownloaderDialogHeader.xib in Resources */,
6741A96C1BF340DE002C974C /* MWMDownloadTransitMapAlert.xib in Resources */,
47A6F3C4235F47B90053FBA4 /* BookmarksSubscriptionViewController.xib in Resources */,
47A6F3C4235F47B90053FBA4 /* CitySubscriptionViewController.xib in Resources */,
F653CE0E1C6DEB2E00A453F1 /* MWMDropDown.xib in Resources */,
34D3B0241E389D05004100F9 /* MWMEditorAddAdditionalNameTableViewCell.xib in Resources */,
34AB667A1FC5AA330078E451 /* RoutePreviewTaxiCell.xib in Resources */,
@ -5380,7 +5420,6 @@
348A8DF51F66775A00D83026 /* RatingView.swift in Sources */,
F63AF50F1EA6215100A1DB98 /* FilterPriceCategoryCell.swift in Sources */,
993DF12123F6BDB100AC231A /* UIViewControllerRenderer.swift in Sources */,
47A6F3C6235F47B90053FBA4 /* BookmarksSubscriptionViewController.swift in Sources */,
34D3AFF61E37A36A004100F9 /* UICollectionView+Cells.swift in Sources */,
473464A7218B0BC000D6AF5B /* MWMPurchaseValidation.mm in Sources */,
4767CDA420AAF66B00BD8166 /* NSAttributedString+HTML.swift in Sources */,
@ -5496,6 +5535,7 @@
B33D21AC20DA515800BAD749 /* MWMCategoryInfoCell.mm in Sources */,
9989273A2449E60200260CE2 /* BottomMenuViewController.swift in Sources */,
473CBF9B2164DD470059BD54 /* SettingsTableViewSelectableProgressCell.swift in Sources */,
99BFEF0224B48D7600A65F5B /* SubscriptionRouter.swift in Sources */,
47E3C72D2111E6A2008B3B27 /* FadeTransitioning.swift in Sources */,
34845DAF1E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift in Sources */,
993DF0B523F6B2EF00AC231A /* PlacePageElevationLayout.swift in Sources */,
@ -5663,6 +5703,7 @@
F63AF5131EA6250F00A1DB98 /* FilterCollectionHolderCell.swift in Sources */,
34AB663E1FC5AA330078E451 /* RouteManagerTransitioning.swift in Sources */,
993F550B237C622700545511 /* DeepLinkIncorrectStrategy.swift in Sources */,
99BFEF0524B48D7600A65F5B /* SubscriptionInteractor.swift in Sources */,
34BBD65C1F826FD30070CA50 /* AuthorizationiPhonePresentationController.swift in Sources */,
56C74C391C74A3BC00B71B9F /* MWMInputEmailValidator.m in Sources */,
479603732446F17C00F3BDD0 /* User+AppleId.swift in Sources */,
@ -5694,6 +5735,7 @@
99012847243F0D6900C72B10 /* UIViewController+alternative.swift in Sources */,
995739062355CAC40019AEE7 /* ImageViewCrossDisolve.swift in Sources */,
47B9065221C7FA400079C85E /* MWMWebImage.m in Sources */,
99EBF73324B4C91000FE1F1F /* SubscriptionViewProtocol.swift in Sources */,
F6E2FE7C1E097BA00083EBEC /* MWMPlacePageOpeningHoursCell.mm in Sources */,
340E1EFB1E2F614400CE49BF /* Storyboard.swift in Sources */,
34E776331F15FAC2003040B3 /* MWMPlacePageManagerHelper.mm in Sources */,
@ -5778,11 +5820,13 @@
33BCDF8B218C976D00EF5B74 /* TagsCollectionViewLayout.swift in Sources */,
6741AA0B1BF340DE002C974C /* MWMMapViewControlsManager.mm in Sources */,
F6E2FED91E097BA00083EBEC /* MWMSearchContentView.m in Sources */,
99EBF73024B4C89000FE1F1F /* AllPassSubscriptionBuilder.swift in Sources */,
F6BD1D211CA412920047B8E8 /* MWMOsmAuthAlert.mm in Sources */,
47CF2E6323BA0DD500D11C30 /* CopyLabel.swift in Sources */,
34AB66321FC5AA330078E451 /* RouteManagerHeaderView.swift in Sources */,
347040301EA6470700038379 /* BorderedButton.swift in Sources */,
F6E2FF4B1E097BA00083EBEC /* SettingsTableViewSwitchCell.swift in Sources */,
99EBF72E24B4C89000FE1F1F /* AllPassSubscriptionViewController.swift in Sources */,
472E3F4A2146C4CD0020E412 /* MWMPurchaseManager.mm in Sources */,
34ABA6211C2D517500FE1BEC /* MWMInputValidator.m in Sources */,
47C7F97521930F5300C2760C /* IInAppBilling.swift in Sources */,
@ -5802,7 +5846,6 @@
F6E2FF301E097BA00083EBEC /* MWMSearchCommonCell.mm in Sources */,
F655C027207278300048A241 /* DiscoveryMoreCell.swift in Sources */,
337F98B821D3D67E00C8AC27 /* SearchHistoryQueryCell.swift in Sources */,
99536111235DABB1008B218F /* BaseSubscriptionViewController.swift in Sources */,
34AB66621FC5AA330078E451 /* TransportTransitSeparator.swift in Sources */,
CDCA2743223F8D1E00167D87 /* ListItemInfo.swift in Sources */,
B32FE74020D2844600EF7446 /* DownloadedBookmarksViewController.swift in Sources */,
@ -5894,8 +5937,10 @@
4747045424622EF0006E51E9 /* GuidesGalleryStyleSheet.swift in Sources */,
3404754D1E081A4600C92850 /* MWMKeyboard.m in Sources */,
993DF10C23F6BDB100AC231A /* MWMTableViewCellRenderer.swift in Sources */,
99BFEF0124B48D7600A65F5B /* SubscriptionPresenter.swift in Sources */,
3457C4261F680F1900028233 /* String+BoundingRect.swift in Sources */,
34EF94291C05A6F30050B714 /* MWMSegue.m in Sources */,
99BFEF0324B48D7600A65F5B /* CitySubscriptionViewController.swift in Sources */,
CD96C70C22A681C400DB7CFE /* DiscoveryGuideCell.swift in Sources */,
47E3C7312111F4C2008B3B27 /* CoverVerticalPresentationAnimator.swift in Sources */,
99E2B0122368A8C700FFABC5 /* MWMCategory+PlacesCountTitle.swift in Sources */,
@ -5925,7 +5970,6 @@
3454D7D71E07F045004AF2AD /* UIKitCategories.m in Sources */,
47E6CB0B2178BA3600EA102B /* SearchBannerCell.swift in Sources */,
34AB39C21D2BD8310021857D /* MWMStopButton.m in Sources */,
995739082355CB660019AEE7 /* AllPassSubscriptionViewController.swift in Sources */,
3488B01A1E9D0B230068AFD8 /* UIColor+Modifications.swift in Sources */,
6741AA281BF340DE002C974C /* MWMAlert.mm in Sources */,
F6E2FF571E097BA00083EBEC /* MWMMobileInternetViewController.m in Sources */,
@ -5967,6 +6011,7 @@
F5BD2CA4DBEFACBC48195F39 /* DiscoveryCollectionHolderCell.swift in Sources */,
4796037524482E3900F3BDD0 /* KeychainStorage.swift in Sources */,
471A7BC4248471BE00A0D4C1 /* BookmarkUIUtils.swift in Sources */,
99BFEF0424B48D7600A65F5B /* CitySubscriptionBuilder.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -0,0 +1,27 @@
class AllPassSubscriptionBuilder {
static func build(parentViewController: UIViewController,
source: String,
successDialog: SubscriptionSuccessDialog,
subscriptionGroupType: SubscriptionGroupType,
completion: ((Bool) -> Void)?) -> UIViewController {
let viewController = AllPassSubscriptionViewController(nibName: nil, bundle: nil)
let router = SubscriptionRouter(viewController: viewController,
parentViewController: parentViewController,
successDialog: successDialog,
subscriptionGroupType: subscriptionGroupType,
completion: completion)
let interactor = SubscriptionInteractor(viewController: viewController,
subscriptionManager: InAppPurchase.allPassSubscriptionManager,
bookmarksManager: BookmarksManager.shared())
let presenter = SubscriptionPresenter(view: viewController,
router: router,
interactor: interactor,
subscriptionManager: InAppPurchase.allPassSubscriptionManager,
source: source)
interactor.presenter = presenter
viewController.presenter = presenter
return viewController
}
}

View file

@ -1,6 +1,6 @@
class AllPassSubscriptionViewController: BaseSubscriptionViewController {
// MARK: outlets
class AllPassSubscriptionViewController: UIViewController {
var presenter: SubscriptionPresenterProtocol!
@IBOutlet private var backgroundImageView: ImageViewCrossDisolve!
@IBOutlet private var annualSubscriptionButton: BookmarksSubscriptionButton!
@IBOutlet private var annualDiscountLabel: InsetsLabel!
@ -9,54 +9,53 @@ class AllPassSubscriptionViewController: BaseSubscriptionViewController {
@IBOutlet private var contentView: UIView!
@IBOutlet private var statusBarBackgroundView: UIVisualEffectView!
@IBOutlet private var descriptionSubtitles: [UILabel]!
//MARK: locals
@IBOutlet private var loadingView: UIView!
override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return [.portrait] }
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
private let transitioning = FadeTransitioning<IPadModalPresentationController>()
private var pageWidth: CGFloat {
return descriptionPageScrollView.frame.width
}
private let maxPages = 3
private var currentPage: Int {
return Int(descriptionPageScrollView.contentOffset.x / pageWidth) + 1
}
private var animatingTask: DispatchWorkItem?
private let animationDelay: TimeInterval = 2
private let animationDuration: TimeInterval = 0.75
private let animationBackDuration: TimeInterval = 0.3
private let statusBarBackVisibleThreshold: CGFloat = 60
override var subscriptionManager: ISubscriptionManager? { return InAppPurchase.allPassSubscriptionManager }
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
if UIDevice.current.userInterfaceIdiom == .pad {
transitioningDelegate = transitioning
modalPresentationStyle = .custom
} else {
modalPresentationStyle = .fullScreen
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
presenter?.configure()
backgroundImageView.images = [
UIImage(named: "AllPassSubscriptionBg1"),
UIImage(named: "AllPassSubscriptionBg2"),
UIImage(named: "AllPassSubscriptionBg3")
]
startAnimating()
annualSubscriptionButton.config(title: L("annual_subscription_title"),
price: "...",
enabled: false)
monthlySubscriptionButton.config(title: L("montly_subscription_title"),
price: "...",
enabled: false)
annualDiscountLabel.isHidden = true
let fontSize: CGFloat = UIScreen.main.bounds.width > 320 ? 17.0 : 14.0
let fontFamily = UIFont.systemFont(ofSize: fontSize).familyName
let css = "<style type=\"text/css\">b{font-weight: 900;}body{font-weight: 300; font-size: \(fontSize); font-family: '-apple-system','\(fontFamily)';}</style>"
@ -65,22 +64,75 @@ class AllPassSubscriptionViewController: BaseSubscriptionViewController {
"all_pass_subscription_message_subtitle_2"]).forEach { title, loc in
title.attributedText = NSAttributedString.string(withHtml: css + L(loc), defaultAttributes: [:])
}
configure(buttons: [
.year: annualSubscriptionButton,
.month: monthlySubscriptionButton],
discountLabels:[
.year: annualDiscountLabel])
preferredContentSize = CGSize(width: 414, height: contentView.frame.height)
}
@IBAction func onAnnualButtonTap(_ sender: UIButton) {
purchase(sender: sender, period: .year)
presenter.purchase(anchor: sender, period: .year)
}
@IBAction func onMonthlyButtonTap(_ sender: UIButton) {
purchase(sender: sender, period: .month)
presenter.purchase(anchor: sender, period: .month)
}
@IBAction func onClose(_ sender: UIButton) {
presenter.onClose()
}
@IBAction func onTerms(_ sender: UIButton) {
presenter.onTermsPressed()
}
@IBAction func onPrivacy(_ sender: UIButton) {
presenter.onPrivacyPressed()
}
}
extension AllPassSubscriptionViewController: SubscriptionViewProtocol {
var isLoadingHidden: Bool {
get {
return loadingView.isHidden
}
set {
loadingView.isHidden = newValue
}
}
func setModel(_ model: SubscriptionViewModel) {
switch model {
case .loading:
annualSubscriptionButton.config(title: L("annual_subscription_title"),
price: "...",
enabled: false)
monthlySubscriptionButton.config(title: L("montly_subscription_title"),
price: "...",
enabled: false)
annualDiscountLabel.isHidden = true
case .subsctiption(let subscriptionData):
for data in subscriptionData {
if data.period == .month {
monthlySubscriptionButton.config(title: data.title,
price: data.price,
enabled: true)
}
if data.period == .year {
annualSubscriptionButton.config(title: data.title,
price: data.price,
enabled: true)
annualDiscountLabel.isHidden = !data.hasDiscount
annualDiscountLabel.text = data.discount
}
}
case .trial(_):
assertionFailure()
}
}
}
extension AllPassSubscriptionViewController: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
presenter.onCancel()
}
}
@ -91,7 +143,7 @@ extension AllPassSubscriptionViewController {
DispatchQueue.main.asyncAfter(deadline: .now() + withDelay, execute: execute)
}
}
private func startAnimating() {
if animatingTask != nil {
animatingTask?.cancel()
@ -103,13 +155,13 @@ extension AllPassSubscriptionViewController {
}
perform(withDelay: animationDelay, execute: animatingTask)
}
private func stopAnimating() {
animatingTask?.cancel()
animatingTask = nil
view.layer.removeAllAnimations()
}
private func scrollToWithAnimation(page: Int, completion: @escaping () -> Void) {
var nextPage = page
var duration = animationDuration
@ -117,7 +169,7 @@ extension AllPassSubscriptionViewController {
nextPage = 1
duration = animationBackDuration
}
let xOffset = CGFloat(nextPage - 1) * pageWidth
UIView.animate(withDuration: duration,
delay: 0,
@ -140,11 +192,11 @@ extension AllPassSubscriptionViewController: UIScrollViewDelegate {
statusBarBackgroundView.alpha = statusBarAlpha
}
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
stopAnimating()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
startAnimating()
}

View file

@ -0,0 +1,27 @@
class CitySubscriptionBuilder {
static func build(parentViewController: UIViewController,
source: String,
successDialog: SubscriptionSuccessDialog,
subscriptionGroupType: SubscriptionGroupType,
completion: ((Bool) -> Void)?) -> UIViewController {
let viewController = CitySubscriptionViewController(nibName: nil, bundle: nil)
let router = SubscriptionRouter(viewController: viewController,
parentViewController: parentViewController,
successDialog: successDialog,
subscriptionGroupType: subscriptionGroupType,
completion: completion)
let interactor = SubscriptionInteractor(viewController: viewController,
subscriptionManager: InAppPurchase.bookmarksSubscriptionManager,
bookmarksManager: BookmarksManager.shared())
let presenter = SubscriptionPresenter(view: viewController,
router: router,
interactor: interactor,
subscriptionManager: InAppPurchase.bookmarksSubscriptionManager,
source: source)
interactor.presenter = presenter
viewController.presenter = presenter
return viewController
}
}

View file

@ -0,0 +1,99 @@
class CitySubscriptionViewController: MWMViewController {
var presenter: SubscriptionPresenterProtocol!
@IBOutlet private var annualSubscriptionButton: BookmarksSubscriptionButton!
@IBOutlet private var annualDiscountLabel: InsetsLabel!
@IBOutlet private var monthlySubscriptionButton: BookmarksSubscriptionButton!
@IBOutlet private var contentView: UIView!
@IBOutlet private var loadingView: UIView!
override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return [.portrait] }
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
private var transitioning = FadeTransitioning<IPadModalPresentationController>()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
if UIDevice.current.userInterfaceIdiom == .pad {
transitioningDelegate = transitioning
modalPresentationStyle = .custom
} else {
modalPresentationStyle = .fullScreen
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
presenter?.configure()
}
@IBAction func onAnnualButtonTap(_ sender: UIButton) {
presenter.purchase(anchor: sender, period: .year)
}
@IBAction func onMonthlyButtonTap(_ sender: UIButton) {
presenter.purchase(anchor: sender, period: .month)
}
@IBAction func onClose(_ sender: UIButton) {
presenter.onClose()
}
@IBAction func onTerms(_ sender: UIButton) {
presenter.onTermsPressed()
}
@IBAction func onPrivacy(_ sender: UIButton) {
presenter.onPrivacyPressed()
}
}
extension CitySubscriptionViewController: SubscriptionViewProtocol {
var isLoadingHidden: Bool {
get {
return loadingView.isHidden
}
set {
loadingView.isHidden = newValue
}
}
func setModel(_ model: SubscriptionViewModel) {
switch model {
case .loading:
annualSubscriptionButton.config(title: L("annual_subscription_title"),
price: "...",
enabled: false)
monthlySubscriptionButton.config(title: L("montly_subscription_title"),
price: "...",
enabled: false)
annualDiscountLabel.isHidden = true
case .subsctiption(let subscriptionData):
for data in subscriptionData {
if data.period == .month {
monthlySubscriptionButton.config(title: data.title,
price: data.price,
enabled: true)
}
if data.period == .year {
annualSubscriptionButton.config(title: data.title,
price: data.price,
enabled: true)
annualDiscountLabel.isHidden = !data.hasDiscount
annualDiscountLabel.text = data.discount
}
}
case .trial(_):
assertionFailure()
}
}
}
extension CitySubscriptionViewController: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
presenter.onCancel()
}
}

View file

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="BookmarksSubscriptionViewController" customModule="maps_me" customModuleProvider="target">
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CitySubscriptionViewController" customModule="maps_me" customModuleProvider="target">
<connections>
<outlet property="annualDiscountLabel" destination="YH0-h4-ZEb" id="onk-lS-6im"/>
<outlet property="annualSubscriptionButton" destination="neX-0h-hs4" id="I6s-8S-Qks"/>

View file

@ -0,0 +1,107 @@
protocol SubscriptionInteractorProtocol: class {
func purchase(anchor: UIView, subscription: ISubscription)
func restore(anchor: UIView)
}
class SubscriptionInteractor {
weak var presenter: SubscriptionPresenterProtocol!
private weak var viewController: UIViewController?
private let subscriptionManager: ISubscriptionManager
private let bookmarksManager: BookmarksManager
init (viewController: UIViewController,
subscriptionManager: ISubscriptionManager,
bookmarksManager: BookmarksManager) {
self.viewController = viewController
self.subscriptionManager = subscriptionManager
self.bookmarksManager = bookmarksManager
}
deinit {
subscriptionManager.removeListener(self)
}
}
extension SubscriptionInteractor: SubscriptionInteractorProtocol {
func purchase(anchor: UIView, subscription: ISubscription) {
subscriptionManager.addListener(self)
viewController?.signup(anchor: anchor, source: .subscription) { [weak self] success in
guard success else { return }
self?.presenter.isLoadingHidden = false
self?.bookmarksManager.ping { success in
guard success else {
self?.presenter.isLoadingHidden = true
let errorDialog = SubscriptionFailViewController { [weak self] in
self?.presenter.onCancel()
}
self?.viewController?.present(errorDialog, animated: true)
return
}
self?.subscriptionManager.subscribe(to: subscription)
}
}
Statistics.logEvent(kStatInappSelect, withParameters: [kStatPurchase: subscriptionManager.serverId,
kStatProduct: subscription.productId],
with: .realtime)
Statistics.logEvent(kStatInappPay, withParameters: [kStatPurchase: subscriptionManager.serverId ],
with: .realtime)
}
func restore(anchor: UIView) {
subscriptionManager.addListener(self)
Statistics.logEvent(kStatInappRestore, withParameters: [kStatPurchase: subscriptionManager.serverId ])
viewController?.signup(anchor: anchor, source: .subscription) { [weak self] success in
guard success else { return }
self?.presenter.isLoadingHidden = false
self?.subscriptionManager.restore { result in
self?.presenter.isLoadingHidden = true
let alertText: String
switch result {
case .valid:
alertText = L("restore_success_alert")
case .notValid:
alertText = L("restore_no_subscription_alert")
case .serverError, .authError:
alertText = L("restore_error_alert")
@unknown default:
fatalError()
}
MWMAlertViewController.activeAlert().presentInfoAlert(L("restore_subscription"), text: alertText)
}
}
}
}
extension SubscriptionInteractor: SubscriptionManagerListener {
func didFailToValidate() {
presenter.isLoadingHidden = true
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didValidate(_ isValid: Bool) {
presenter.isLoadingHidden = true
if isValid {
presenter.onSubscribe()
} else {
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
}
func didFailToSubscribe(_ subscription: ISubscription, error: Error?) {
presenter.isLoadingHidden = true
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didSubscribe(_ subscription: ISubscription) {
subscriptionManager.setSubscriptionActive(true)
bookmarksManager.resetInvalidCategories()
}
func didDefer(_ subscription: ISubscription) {
}
}

View file

@ -0,0 +1,115 @@
protocol SubscriptionPresenterProtocol: class {
var isLoadingHidden: Bool { get set }
func configure()
func purchase(anchor: UIView, period: SubscriptionPeriod)
func restore(anchor: UIView)
func onTermsPressed()
func onPrivacyPressed()
func onClose()
func onSubscribe()
func onCancel()
}
class SubscriptionPresenter {
private weak var view: SubscriptionViewProtocol?
private let router: SubscriptionRouterProtocol
private let interactor: SubscriptionInteractorProtocol
private var subscriptionGroup: ISubscriptionGroup?
private let subscriptionManager: ISubscriptionManager
private var source: String = kStatWebView
init(view: SubscriptionViewProtocol,
router: SubscriptionRouterProtocol,
interactor: SubscriptionInteractorProtocol,
subscriptionManager: ISubscriptionManager,
source: String) {
self.view = view
self.router = router
self.interactor = interactor
self.subscriptionManager = subscriptionManager
self.source = source
}
}
extension SubscriptionPresenter: SubscriptionPresenterProtocol {
var isLoadingHidden: Bool {
get {
return view?.isLoadingHidden ?? false
}
set {
view?.isLoadingHidden = newValue
}
}
func configure() {
view?.setModel(SubscriptionViewModel.loading)
subscriptionManager.getAvailableSubscriptions { [weak self] subscriptions, error in
self?.view?.isLoadingHidden = true
guard let subscriptions = subscriptions else {
MWMAlertViewController.activeAlert().presentInfoAlert(L("price_error_title"),
text: L("price_error_subtitle"))
self?.onCancel()
return
}
let group = SubscriptionGroup(subscriptions: subscriptions)
self?.subscriptionGroup = group
var data: [SubscriptionViewModel.SubscriptionData] = []
for period in [SubscriptionPeriod.month, SubscriptionPeriod.year] {
guard let subscriptionItem = group[period] else {
assertionFailure()
return
}
data.append(SubscriptionViewModel.SubscriptionData(price: subscriptionItem.formattedPrice,
title: subscriptionItem.title,
period: period,
hasDiscount: subscriptionItem.hasDiscount,
discount: L("all_pass_screen_best_value")))
}
self?.view?.setModel(SubscriptionViewModel.subsctiption(data))
}
Statistics.logEvent(kStatInappShow, withParameters: [kStatVendor: subscriptionManager.vendorId,
kStatPurchase: subscriptionManager.serverId,
kStatProduct: subscriptionManager.productIds[0],
kStatFrom: source], with: .realtime)
}
func purchase(anchor: UIView, period: SubscriptionPeriod) {
guard let subscription = subscriptionGroup?[period]?.subscription else {
return
}
interactor.purchase(anchor: anchor, subscription: subscription)
Statistics.logEvent(kStatInappSelect, withParameters: [kStatPurchase: subscriptionManager.serverId,
kStatProduct: subscription.productId],
with: .realtime)
Statistics.logEvent(kStatInappPay, withParameters: [kStatPurchase: subscriptionManager.serverId ],
with: .realtime)
}
func onTermsPressed() {
router.showTerms()
}
func onPrivacyPressed() {
router.showPrivacy()
}
func onClose() {
router.cancel()
Statistics.logEvent(kStatInappCancel, withParameters: [kStatPurchase: subscriptionManager.serverId])
}
func restore(anchor: UIView) {
interactor.restore(anchor: anchor)
}
func onSubscribe() {
router.subscribe()
}
func onCancel() {
router.cancel()
}
}

View file

@ -0,0 +1,78 @@
import SafariServices
protocol SubscriptionRouterProtocol: class {
func showTerms()
func showPrivacy()
func subscribe()
func cancel()
}
enum SubscriptionSuccessDialog {
case goToCatalog
case success
case none
}
class SubscriptionRouter {
private weak var viewController: UIViewController?
private weak var parentViewController: UIViewController?
private var successDialog: SubscriptionSuccessDialog
private var completion:((Bool) -> Void)?
private var subscriptionGroupType: SubscriptionGroupType
init(viewController: UIViewController,
parentViewController: UIViewController,
successDialog: SubscriptionSuccessDialog,
subscriptionGroupType: SubscriptionGroupType,
completion: ((Bool) -> Void)?) {
self.viewController = viewController
self.parentViewController = viewController
self.successDialog = successDialog
self.completion = completion
self.subscriptionGroupType = subscriptionGroupType
}
}
extension SubscriptionRouter: SubscriptionRouterProtocol{
func showTerms() {
guard let url = URL(string: User.termsOfUseLink()) else { return }
let safari = SFSafariViewController(url: url)
viewController?.present(safari, animated: true, completion: nil)
}
func showPrivacy() {
guard let url = URL(string: User.privacyPolicyLink()) else { return }
let safari = SFSafariViewController(url: url)
viewController?.present(safari, animated: true, completion: nil)
}
func subscribe() {
parentViewController?.dismiss(animated: true) {[weak self] in
self?.completion?(true);
}
switch successDialog {
case .goToCatalog:
let successDialog = SubscriptionGoToCatalogViewController(subscriptionGroupType, onOk: {[weak self] in
self?.parentViewController?.dismiss(animated: true)
let webViewController = CatalogWebViewController.catalogFromAbsoluteUrl(nil, utm: .none)
self?.parentViewController?.navigationController?.pushViewController(webViewController, animated: true)
}) {
self.parentViewController?.dismiss(animated: true)
}
parentViewController?.present(successDialog, animated: true)
case .success:
let successDialog = SubscriptionSuccessViewController(subscriptionGroupType) {[weak self] in
self?.parentViewController?.dismiss(animated: true)
}
parentViewController?.present(successDialog, animated: true)
case .none:
break;
}
}
func cancel() {
parentViewController?.dismiss(animated: true) { [weak self] in
self?.completion?(false)
}
}
}

View file

@ -0,0 +1,22 @@
class SubscriptionViewBuilder {
static func build(type: SubscriptionGroupType,
parentViewController: UIViewController,
source: String,
successDialog: SubscriptionSuccessDialog,
completion: ((Bool) -> Void)?) -> UIViewController {
switch type {
case .city:
return CitySubscriptionBuilder.build(parentViewController: parentViewController,
source: source,
successDialog: successDialog,
subscriptionGroupType: type,
completion: completion)
case .allPass:
return AllPassSubscriptionBuilder.build(parentViewController: parentViewController,
source: source,
successDialog: successDialog,
subscriptionGroupType: type,
completion: completion)
}
}
}

View file

@ -0,0 +1,23 @@
protocol SubscriptionViewProtocol: class {
var isLoadingHidden: Bool { get set }
var presenter: SubscriptionPresenterProtocol! { get set }
func setModel(_ model: SubscriptionViewModel)
}
enum SubscriptionViewModel {
struct TrialData {
}
struct SubscriptionData {
let price: String
let title: String
let period: SubscriptionPeriod
let hasDiscount: Bool
let discount: String
}
case loading
case subsctiption([SubscriptionData])
case trial(TrialData)
}