Skip to content

Commit

Permalink
Add discount banner to Vault List.
Browse files Browse the repository at this point in the history
  • Loading branch information
iammajid committed Nov 29, 2024
1 parent 56056cd commit 534ca9d
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 0 deletions.
115 changes: 115 additions & 0 deletions Cryptomator/VaultList/VaultListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ class VaultListViewController: ListViewController<VaultCellViewModel> {
private let viewModel: VaultListViewModelProtocol
private var observer: NSObjectProtocol?
@Dependency(\.fullVersionChecker) private var fullVersionChecker
@Dependency(\.cryptomatorSettings) private var cryptomatorSettings

private var bannerView: UIView?
private var bannerDismissed: Bool {
get {
return CryptomatorUserDefaults.shared.bannerDismissed
}
set {
CryptomatorUserDefaults.shared.bannerDismissed = newValue
}
}

init(with viewModel: VaultListViewModelProtocol) {
self.viewModel = viewModel
Expand Down Expand Up @@ -49,6 +60,8 @@ class VaultListViewController: ListViewController<VaultCellViewModel> {
DDLogError("Refresh vault lock states failed with error: \(error)")
}
}

checkAndShowBanner()
}

override func viewWillAppear(_ animated: Bool) {
Expand Down Expand Up @@ -108,4 +121,106 @@ class VaultListViewController: ListViewController<VaultCellViewModel> {
coordinator?.showVaultDetail(for: vaultCellViewModel.vault)
}
}

// MARK: - Discount Banner

private func checkAndShowBanner() {
let calendar = Calendar.current
let isTargetMonth = calendar.component(.year, from: Date()) == 2024 &&
calendar.component(.month, from: Date()) == 11

if isTargetMonth &&
!(cryptomatorSettings.fullVersionUnlocked || cryptomatorSettings.hasRunningSubscription) &&
!bannerDismissed {
showBanner()
}
}

private func showBanner() {
let banner = UIView()
banner.backgroundColor = UIColor.cryptomatorPrimary
banner.translatesAutoresizingMaskIntoConstraints = false
banner.layer.cornerRadius = 12
banner.layer.masksToBounds = true

let emojiLabel = UILabel()
emojiLabel.text = "🎁"
emojiLabel.translatesAutoresizingMaskIntoConstraints = false

let textLabel = UILabel()
textLabel.text = LocalizedString.getValue("purchase.discount")
textLabel.textColor = .white
textLabel.font = UIFont.boldSystemFont(ofSize: 14)
textLabel.translatesAutoresizingMaskIntoConstraints = false

let dismissButton = UIButton(type: .system)
dismissButton.setTitle("X", for: .normal)
dismissButton.setTitleColor(.white, for: .normal)
dismissButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12)
dismissButton.addTarget(self, action: #selector(dismissBanner), for: .touchUpInside)
dismissButton.translatesAutoresizingMaskIntoConstraints = false

banner.addSubview(emojiLabel)
banner.addSubview(textLabel)
banner.addSubview(dismissButton)

NSLayoutConstraint.activate([
emojiLabel.leadingAnchor.constraint(equalTo: banner.leadingAnchor, constant: 16),
emojiLabel.centerYAnchor.constraint(equalTo: banner.centerYAnchor),

textLabel.leadingAnchor.constraint(equalTo: emojiLabel.trailingAnchor, constant: 8),
textLabel.centerYAnchor.constraint(equalTo: banner.centerYAnchor),

dismissButton.leadingAnchor.constraint(equalTo: textLabel.trailingAnchor, constant: 8),
dismissButton.trailingAnchor.constraint(equalTo: banner.trailingAnchor, constant: -16),
dismissButton.centerYAnchor.constraint(equalTo: banner.centerYAnchor)
])

let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(bannerTapped))
banner.addGestureRecognizer(tapGestureRecognizer)

view.addSubview(banner)

NSLayoutConstraint.activate([
banner.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
banner.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
banner.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
banner.centerXAnchor.constraint(equalTo: view.centerXAnchor),
banner.heightAnchor.constraint(equalToConstant: 60)
])

bannerView = banner
}

@objc private func dismissBanner() {
bannerView?.removeFromSuperview()
bannerDismissed = true
}

@objc private func bannerTapped() {
let purchaseViewModel = PurchaseViewModel()
let purchaseViewController = PurchaseViewController(viewModel: purchaseViewModel)
purchaseViewController.modalPresentationStyle = .pageSheet

let navigationController = UINavigationController(rootViewController: purchaseViewController)
navigationController.modalPresentationStyle = .pageSheet

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = UIColor.cryptomatorPrimary
appearance.titleTextAttributes = [.foregroundColor: UIColor.white]

navigationController.navigationBar.standardAppearance = appearance
navigationController.navigationBar.scrollEdgeAppearance = appearance
navigationController.navigationBar.tintColor = .white

let closeButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissIAPView))
purchaseViewController.navigationItem.rightBarButtonItem = closeButton

present(navigationController, animated: true, completion: nil)
}

@objc private func dismissIAPView() {
dismiss(animated: true, completion: nil)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public protocol CryptomatorSettings {
var trialExpirationDate: Date? { get set }
var fullVersionUnlocked: Bool { get set }
var hasRunningSubscription: Bool { get set }
var bannerDismissed: Bool { get set }
}

private enum CryptomatorSettingsKey: DependencyKey {
Expand Down Expand Up @@ -108,4 +109,14 @@ extension CryptomatorUserDefaults: CryptomatorSettings {
get { read() ?? false }
set { write(value: newValue) }
}

public var bannerDismissed: Bool {
get {
let value = read(property: "bannerDismissed") ?? false
return value
}
set {
write(value: newValue, to: "bannerDismissed")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ class CryptomatorSettingsMock: CryptomatorSettings {
var debugModeEnabled: Bool = false
var fullVersionUnlocked: Bool = false
var hasRunningSubscription: Bool = false
var bannerDismissed: Bool = false
}
#endif
1 change: 1 addition & 0 deletions SharedResources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"onboarding.button.continue" = "Continue";

"purchase.beginFreeTrial.alert.title" = "Trial Unlocked";
"purchase.discount" = "Full Version IAP is 30% off in December";
"purchase.expiredTrial" = "Your trial has expired.";
"purchase.footer.privacyPolicy" = "Privacy Policy";
"purchase.footer.termsOfUse" = "Terms of Use";
Expand Down

0 comments on commit 534ca9d

Please sign in to comment.