Skip to content

Commit dd80f1a

Browse files
authored
Merge pull request #443 from woocommerce/issue/19-green-dot
Notifications: Adds the green dot badge to the notification tab
2 parents d107e3f + 6761310 commit dd80f1a

File tree

5 files changed

+173
-4
lines changed

5 files changed

+173
-4
lines changed

WooCommerce/Classes/AppDelegate.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
8585
}
8686

8787
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
88-
8988
return true
9089
}
9190

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Foundation
2+
import UIKit
3+
4+
5+
/// UIView animation helpers
6+
///
7+
extension UIView {
8+
9+
/// Unhides the current view by applying a fade-in animation.
10+
///
11+
/// - Parameters:
12+
/// - duration: The total duration of the animation, measured in seconds. (defaults to 0.5 seconds)
13+
/// - delay: The amount of time (measured in seconds) to wait before beginning the animation.
14+
/// - completion: The block executed when the animation sequence ends
15+
///
16+
func fadeIn(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: ((Bool) -> Void)? = nil) {
17+
self.alpha = 0.0
18+
19+
UIView.animate(withDuration: duration, delay: delay, options: .curveEaseIn, animations: {
20+
self.isHidden = false
21+
self.alpha = 1.0
22+
}, completion: completion)
23+
}
24+
25+
26+
/// Hides the current view by applying a fade-out animation.
27+
///
28+
/// - Parameters:
29+
/// - duration: The total duration of the animations, measured in seconds. (defaults to 0.5 seconds)
30+
/// - delay: The amount of time (measured in seconds) to wait before beginning the animation.
31+
/// - completion: The block executed when the animation sequence ends
32+
///
33+
func fadeOut(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: ((Bool) -> Void)? = nil) {
34+
self.alpha = 1.0
35+
36+
UIView.animate(withDuration: duration, delay: delay, options: .curveEaseIn, animations: {
37+
self.alpha = 0.0
38+
}) { finished in
39+
self.isHidden = true
40+
completion?(finished)
41+
}
42+
}
43+
}

WooCommerce/Classes/ViewRelated/MainTabBarController.swift

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,98 @@ extension MainTabBarController {
126126
return navController.topViewController as? T
127127
}
128128
}
129+
130+
131+
// MARK: - Tab dot madness!
132+
//
133+
extension MainTabBarController {
134+
135+
static func showDotOn(_ tab: WooTab) {
136+
guard let tabBar = AppDelegate.shared.tabBarController?.tabBar else {
137+
return
138+
}
139+
140+
hideDotOn(tab)
141+
let dot = GreenDotView(frame: CGRect(x: DotConstants.xOffset,
142+
y: DotConstants.yOffset,
143+
width: DotConstants.diameter,
144+
height: DotConstants.diameter), borderWidth: DotConstants.borderWidth)
145+
dot.tag = dotTag(for: tab)
146+
dot.isHidden = true
147+
tabBar.subviews[tab.rawValue].subviews.first?.insertSubview(dot, at: 1)
148+
dot.fadeIn()
149+
}
150+
151+
static func hideDotOn(_ tab: WooTab) {
152+
guard let tabBar = AppDelegate.shared.tabBarController?.tabBar else {
153+
return
154+
}
155+
156+
let tag = dotTag(for: tab)
157+
if let subviews = tabBar.subviews[tab.rawValue].subviews.first?.subviews {
158+
for subview in subviews where subview.tag == tag {
159+
subview.fadeOut() { _ in
160+
subview.removeFromSuperview()
161+
}
162+
}
163+
}
164+
}
165+
166+
private static func dotTag(for tab: WooTab) -> Int {
167+
return tab.rawValue + DotConstants.tagOffset
168+
}
169+
}
170+
171+
172+
// MARK: - Constants!
173+
//
174+
private extension MainTabBarController {
175+
176+
enum DotConstants {
177+
static let diameter = CGFloat(10)
178+
static let borderWidth = CGFloat(1)
179+
static let xOffset = CGFloat(2)
180+
static let yOffset = CGFloat(0)
181+
static let tagOffset = 999
182+
}
183+
}
184+
185+
186+
// MARK: - GreenDot UIView
187+
//
188+
private class GreenDotView: UIView {
189+
190+
private var borderWidth = CGFloat(1) // Border line width defaults to 1
191+
192+
/// Designated Initializer
193+
///
194+
init(frame: CGRect, borderWidth: CGFloat) {
195+
super.init(frame: frame)
196+
self.borderWidth = borderWidth
197+
setupSubviews()
198+
}
199+
200+
/// Required Initializer
201+
///
202+
required init?(coder aDecoder: NSCoder) {
203+
super.init(coder: aDecoder)
204+
setupSubviews()
205+
}
206+
207+
private func setupSubviews() {
208+
backgroundColor = .clear
209+
}
210+
211+
override func draw(_ rect: CGRect) {
212+
let path = UIBezierPath(ovalIn: CGRect(x: rect.origin.x + borderWidth,
213+
y: rect.origin.y + borderWidth,
214+
width: rect.size.width - borderWidth*2,
215+
height: rect.size.height - borderWidth*2))
216+
StyleManager.wooAccent.setFill()
217+
path.fill()
218+
219+
path.lineWidth = borderWidth
220+
StyleManager.wooWhite.setStroke()
221+
path.stroke()
222+
}
223+
}

WooCommerce/Classes/ViewRelated/Notifications/NotificationsViewController.swift

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ class NotificationsViewController: UIViewController {
7575
return resultsController.isEmpty
7676
}
7777

78+
/// The current unread Notes.
79+
///
80+
private var unreadNotes: [Note] {
81+
return resultsController.fetchedObjects.filter { $0.read == false }
82+
}
7883

7984
// MARK: - View Lifecycle
8085

@@ -147,6 +152,14 @@ private extension NotificationsViewController {
147152
///
148153
func configureResultsController() {
149154
resultsController.startForwardingEvents(to: tableView)
155+
resultsController.onDidChangeContent = { [weak self] in
156+
// FIXME: This should be removed once `PushNotificationsManager` is in place
157+
self?.updateNotificationsTabIfNeeded()
158+
}
159+
resultsController.onDidResetContent = {
160+
// FIXME: This should be removed once `PushNotificationsManager` is in place
161+
MainTabBarController.hideDotOn(.notifications)
162+
}
150163
try? resultsController.performFetch()
151164
}
152165

@@ -174,13 +187,12 @@ private extension NotificationsViewController {
174187
}
175188

176189
@IBAction func markAllAsRead() {
177-
let unread = resultsController.fetchedObjects.filter { $0.read == false }
178-
if unread.isEmpty {
190+
if unreadNotes.isEmpty {
179191
DDLogVerbose("# Every single notification is already marked as Read!")
180192
return
181193
}
182194

183-
markAsRead(notes: unread)
195+
markAsRead(notes: unreadNotes)
184196
hapticGenerator.notificationOccurred(.success)
185197
}
186198
}
@@ -447,6 +459,22 @@ private extension NotificationsViewController {
447459
}
448460

449461

462+
// MARK: - Private Helpers
463+
//
464+
private extension NotificationsViewController {
465+
466+
// FIXME: This should be removed once `PushNotificationsManager` is in place
467+
func updateNotificationsTabIfNeeded() {
468+
guard !unreadNotes.isEmpty else {
469+
MainTabBarController.hideDotOn(.notifications)
470+
return
471+
}
472+
473+
MainTabBarController.showDotOn(.notifications)
474+
}
475+
}
476+
477+
450478
// MARK: - Nested Types
451479
//
452480
private extension NotificationsViewController {

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
746791662108D87B007CF1DC /* MockupAnalyticsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 746791652108D87B007CF1DC /* MockupAnalyticsProvider.swift */; };
3232
747AA0892107CEC60047A89B /* AnalyticsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747AA0882107CEC60047A89B /* AnalyticsProvider.swift */; };
3333
747AA08B2107CF8D0047A89B /* TracksProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747AA08A2107CF8D0047A89B /* TracksProvider.swift */; };
34+
748AD087219F481B00023535 /* UIView+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748AD086219F481B00023535 /* UIView+Animation.swift */; };
3435
748C7780211E18A600814F2C /* OrderStats+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748C777F211E18A600814F2C /* OrderStats+Woo.swift */; };
3536
748C7782211E294000814F2C /* Double+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748C7781211E294000814F2C /* Double+Woo.swift */; };
3637
748C7784211E2D8400814F2C /* DoubleWooTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748C7783211E2D8400814F2C /* DoubleWooTests.swift */; };
@@ -269,6 +270,7 @@
269270
746791652108D87B007CF1DC /* MockupAnalyticsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockupAnalyticsProvider.swift; sourceTree = "<group>"; };
270271
747AA0882107CEC60047A89B /* AnalyticsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsProvider.swift; sourceTree = "<group>"; };
271272
747AA08A2107CF8D0047A89B /* TracksProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksProvider.swift; sourceTree = "<group>"; };
273+
748AD086219F481B00023535 /* UIView+Animation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Animation.swift"; sourceTree = "<group>"; };
272274
748C777F211E18A600814F2C /* OrderStats+Woo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderStats+Woo.swift"; sourceTree = "<group>"; };
273275
748C7781211E294000814F2C /* Double+Woo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Woo.swift"; sourceTree = "<group>"; };
274276
748C7783211E2D8400814F2C /* DoubleWooTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleWooTests.swift; sourceTree = "<group>"; };
@@ -942,6 +944,7 @@
942944
B582F95820FFCEAA0060934A /* UITableViewHeaderFooterView+Helpers.swift */,
943945
B57C744D20F56E3800EEFC87 /* UITableViewCell+Helpers.swift */,
944946
B554E1782152F20000F31188 /* UINavigationBar+Appearance.swift */,
947+
748AD086219F481B00023535 /* UIView+Animation.swift */,
945948
B57C744620F55BC800EEFC87 /* UIView+Helpers.swift */,
946949
B5A82EE6210263460053ADC8 /* UIViewController+Helpers.swift */,
947950
B5AA7B3E20ED81C2004DA14F /* UserDefaults+Woo.swift */,
@@ -1558,6 +1561,7 @@
15581561
B582F95920FFCEAA0060934A /* UITableViewHeaderFooterView+Helpers.swift in Sources */,
15591562
B5D1AFBA20BC515600DB0E8C /* UIColor+Woo.swift in Sources */,
15601563
CE1CCB4B20570B1F000EE3AC /* OrderListCell.swift in Sources */,
1564+
748AD087219F481B00023535 /* UIView+Animation.swift in Sources */,
15611565
748D34DF214828DD00E21A2F /* TopPerformersViewController.swift in Sources */,
15621566
B5AA7B3D20ED5D15004DA14F /* SessionManager.swift in Sources */,
15631567
B53B3F37219C75AC00DF1EB6 /* OrderLoaderViewController.swift in Sources */,

0 commit comments

Comments
 (0)