@@ -17,8 +17,14 @@ class VaultListViewController: ListViewController<VaultCellViewModel> {
1717 weak var coordinator : MainCoordinator ?
1818
1919 private let viewModel : VaultListViewModelProtocol
20- private var observer : NSObjectProtocol ?
20+ private var willEnterForegroundObserver : NSObjectProtocol ?
2121 @Dependency ( \. fullVersionChecker) private var fullVersionChecker
22+ @Dependency ( \. cryptomatorSettings) private var cryptomatorSettings
23+
24+ #if !ALWAYS_PREMIUM
25+ private var bannerView : UIView ?
26+ private var fullVersionPurchasedObserver : NSObjectProtocol ?
27+ #endif
2228
2329 init ( with viewModel: VaultListViewModelProtocol ) {
2430 self . viewModel = viewModel
@@ -44,11 +50,18 @@ class VaultListViewController: ListViewController<VaultCellViewModel> {
4450 let addNewVaulButton = UIBarButtonItem ( barButtonSystemItem: . add, target: self , action: #selector( addNewVault) )
4551 navigationItem. rightBarButtonItem = addNewVaulButton
4652
47- observer = NotificationCenter . default. addObserver ( forName: UIApplication . willEnterForegroundNotification, object: nil , queue: . main) { [ weak self] _ in
53+ willEnterForegroundObserver = NotificationCenter . default. addObserver ( forName: UIApplication . willEnterForegroundNotification, object: nil , queue: . main) { [ weak self] _ in
4854 self ? . viewModel. refreshVaultLockStates ( ) . catch { error in
4955 DDLogError ( " Refresh vault lock states failed with error: \( error) " )
5056 }
5157 }
58+
59+ #if !ALWAYS_PREMIUM
60+ fullVersionPurchasedObserver = NotificationCenter . default. addObserver ( forName: . purchasedFullVersionNotification, object: nil , queue: . main) { [ weak self] _ in
61+ self ? . dismissBanner ( )
62+ }
63+ checkAndShowBanner ( )
64+ #endif
5265 }
5366
5467 override func viewWillAppear( _ animated: Bool ) {
@@ -108,4 +121,94 @@ class VaultListViewController: ListViewController<VaultCellViewModel> {
108121 coordinator? . showVaultDetail ( for: vaultCellViewModel. vault)
109122 }
110123 }
124+
125+ // MARK: - Discount Banner
126+
127+ #if !ALWAYS_PREMIUM
128+ private func checkAndShowBanner( ) {
129+ let currentYear = Calendar . current. component ( . year, from: Date ( ) )
130+ let currentMonth = Calendar . current. component ( . month, from: Date ( ) )
131+ if currentYear == 2024 , currentMonth == 12 , !( cryptomatorSettings. fullVersionUnlocked || cryptomatorSettings. hasRunningSubscription) , !cryptomatorSettings. december2024BannerDismissed {
132+ showBanner ( )
133+ }
134+ }
135+
136+ private func showBanner( ) {
137+ let banner = UIView ( )
138+ banner. backgroundColor = UIColor . cryptomatorPrimary
139+ banner. translatesAutoresizingMaskIntoConstraints = false
140+ banner. layer. cornerRadius = 12
141+ banner. layer. masksToBounds = true
142+
143+ let emojiLabel = UILabel ( )
144+ emojiLabel. text = " 🎁 "
145+ emojiLabel. translatesAutoresizingMaskIntoConstraints = false
146+ emojiLabel. setContentHuggingPriority ( . required, for: . horizontal)
147+ emojiLabel. setContentCompressionResistancePriority ( . required, for: . horizontal)
148+
149+ let textLabel = UILabel ( )
150+ textLabel. text = " Lifetime License is 33%* off in December! "
151+ textLabel. textColor = . white
152+ textLabel. font = UIFont . preferredFont ( forTextStyle: . footnote)
153+ textLabel. adjustsFontSizeToFitWidth = true
154+ textLabel. minimumScaleFactor = 0.5
155+ textLabel. numberOfLines = 2
156+ textLabel. translatesAutoresizingMaskIntoConstraints = false
157+
158+ let dismissButton = UIButton ( type: . close)
159+ dismissButton. addTarget ( self , action: #selector( dismissBanner) , for: . touchUpInside)
160+ dismissButton. translatesAutoresizingMaskIntoConstraints = false
161+ dismissButton. setContentHuggingPriority ( . required, for: . horizontal)
162+ dismissButton. setContentCompressionResistancePriority ( . required, for: . horizontal)
163+
164+ banner. addSubview ( emojiLabel)
165+ banner. addSubview ( textLabel)
166+ banner. addSubview ( dismissButton)
167+
168+ NSLayoutConstraint . activate ( [
169+ emojiLabel. leadingAnchor. constraint ( equalTo: banner. leadingAnchor, constant: 16 ) ,
170+ emojiLabel. centerYAnchor. constraint ( equalTo: banner. centerYAnchor) ,
171+
172+ textLabel. leadingAnchor. constraint ( equalTo: emojiLabel. trailingAnchor, constant: 8 ) ,
173+ textLabel. centerYAnchor. constraint ( equalTo: banner. centerYAnchor) ,
174+
175+ dismissButton. leadingAnchor. constraint ( equalTo: textLabel. trailingAnchor, constant: 8 ) ,
176+ dismissButton. trailingAnchor. constraint ( equalTo: banner. trailingAnchor, constant: - 16 ) ,
177+ dismissButton. centerYAnchor. constraint ( equalTo: banner. centerYAnchor)
178+ ] )
179+
180+ let tapGestureRecognizer = UITapGestureRecognizer ( target: self , action: #selector( bannerTapped) )
181+ banner. addGestureRecognizer ( tapGestureRecognizer)
182+
183+ view. addSubview ( banner)
184+
185+ NSLayoutConstraint . activate ( [
186+ banner. leadingAnchor. constraint ( equalTo: view. leadingAnchor, constant: 16 ) ,
187+ banner. trailingAnchor. constraint ( equalTo: view. trailingAnchor, constant: - 16 ) ,
188+ banner. bottomAnchor. constraint ( equalTo: view. safeAreaLayoutGuide. bottomAnchor, constant: - 16 ) ,
189+ banner. centerXAnchor. constraint ( equalTo: view. centerXAnchor) ,
190+ banner. heightAnchor. constraint ( equalToConstant: 50 )
191+ ] )
192+
193+ bannerView = banner
194+ }
195+
196+ @objc private func dismissBanner( ) {
197+ UIView . animate ( withDuration: 0.3 , animations: {
198+ self . bannerView? . alpha = 0
199+ } , completion: { _ in
200+ self . bannerView? . removeFromSuperview ( )
201+ self . bannerView = nil
202+ } )
203+ CryptomatorUserDefaults . shared. december2024BannerDismissed = true
204+ }
205+
206+ @objc private func bannerTapped( ) {
207+ coordinator? . showPurchase ( )
208+ }
209+ #endif
210+ }
211+
212+ extension Notification . Name {
213+ static let purchasedFullVersionNotification = Notification . Name ( " PurchasedFullVersionNotification " )
111214}
0 commit comments